Embodiments of the present application relate generally to application programs using a database to provide persistent storage of data, and more particularly to applications that can mutate polymorphic object components.
Many application programs, particularly web based application programs, are increasingly designed using an n-tier architecture. In an n-tier architecture, each tier is generally responsible for certain functionality, and the functionality provided by a tier is independent of the other tiers. For example, a typical web application may involve three tiers. The top tier may be exposed to the user, and is typically referred to as the presentation tier. Functionality in the presentation tier can include providing a display on a user's computer browser. The presentation tier may also be responsible for providing forms which a user may use to input data.
An exemplary middle tier may provide verification and validation logic that is used to verify and validate the user's input as provided by the presentation tier, prior to being stored in persistent storage. In addition, the middle tier may extract data from the persistent storage for use by the presentation tier. Typically, the middle tier is implemented using standard object oriented programming techniques. Data values as provided to and from the presentation tier may be represented as attribute values of one or more software objects. Operations on the attribute values may be provided by methods associated with those objects. The middle tier may also contain business logic to operate on the data received from the presentation tier. The software objects will typically provide a mechanism for interfacing with a persistent storage to store attribute values of the software objects in a database.
An exemplary third tier corresponds to persistent storage that may be used to store data. Such persistent storage may be provided in the form of a database. A wide variety of databases are available from numerous vendors, including those provided by the assignee of the present application. A typical relational database may comprise one or more tables, each table consisting of one or more rows and having one or more columns. A logical mapping typically exists between the database table and the corresponding software objects. In a simple scenario, there is a one to one mapping between a single database table and a corresponding software object. Each column may map to an attribute of a software object, while a row may represent a record comprising the complete set of attribute values for the software object.
In a more complex scenario, the data in a set of inter-related tables represent a combined atomic software object. Such a software object may need to be represented by several constituent software objects which may be referred to as software object components. If the relationship is hierarchical in nature e.g. an invoice and its invoiced items, then the corresponding software object components may also need to be designed in a parent-child relationship.
In a different complex scenario, one database table may itself house the data of several similar but distinct software object types. A single database table may contain columns that map to attributes for multiple different object types, each object type containing a different set attributes. Such a scheme is generally referred to as polymorphism. One or more additional attributes, referred to as discriminator attributes, may be included in the object types, and map to a column in the database. The discriminator attributes allow the application to determine the appropriate object type for the data stored in a particular row of the table. A typical design for such an arrangement entails multiple software object components, each representing the various software object types, themselves inheriting from a common parent software object component.
A highly complex scenario could be a medley of both complexities above, i.e. a software object may consist of several software object components arranged in a multi-level deep hierarchy (e.g. parent-child-grandchild-great-grandchild relationship) whereby nodes in each level are further expressed via polymorphic types.
Despite such complexity, a presentation tier may still attempt to provide a quick entry screen for such a complex software object. When creating a new object, users would begin by choosing the type of the top level software object component from the presentation layer. Such a selection may be in the form of choosing a value for a discriminator attribute. The presentation tier may then prompt the middle tier to instantiate a software object that may be utilized to interface with the persistent storage tier. The data values of the attributes of the software object may be stored in one or more tables provided by the third tier. As explained above, the software object may contain logic to ensure that data being entered by the user through the presentation tier is appropriate for the instantiated software object.
Situations may arise where a user has chosen an object of a particular type, and has begun entry of data corresponding to the attributes of that instantiated object. The user may then realize he has chosen an object of an incorrect type. In some cases, the attributes of the incorrectly chosen software object may also be applicable to the software object of the correct type. However, the software object of the incorrect type selected by the user cannot simply be mutated to an object of the correct type. In order to store the data in the object of the correct type, the user would generally be required to discard all of the data that has already been entered for the object of the incorrect type, create an object of the correct type, and then reenter the data for the attributes of the object of the correct type that is newly instantiated.
For example, a user may specify to the presentation tier that he or she wishes to create an invoice object. The middle tier may then instantiate an invoice object. The user may begin entering certain attributes, such as an address of a business that should receive the invoice. At some point, the user may realize he did not intend to create an invoice object, but rather intended to create a different type of object, such as a freight invoice object. However, the invoice object that has already been instantiated would be unsuitable for use as a freight invoice object, despite the fact that both invoices would share attributes such as the address of the business. The user would be forced to start over, and specify a freight invoice object and re-enter data for the freight invoice object.
In other situations, the user may not know the correct type of object to create at the time of creation. For example, a sales representative who is on the road may wish to record all of his expenses using a device such as a personal digital assistant. At the time of entering the expense data, the sales representative may not know the specific type of object to be used for storing the expenses. Accordingly, the user may use an object of a temporary or generic type for storing the expenses. The expense data is thus stored in objects instantiated of a generic type. These would then have to be converted to objects of the correct data type in order for the data stored by the temporary/generic objects to be synchronized with data in a database. When the sales representative is back in the office, he or she may be in a better position to classify each temporary or generic instantiated object to an object of the correct type. However, just as discussed above, once a generic expense object has been instantiated, it is not possible to simply mutate it to an object of the correct object type. The user is forced to create an object of the correct type and reenter the data for the attributes of the object of the correct type.